#define INLJ1O 0x1	/*** T1 outer use T2 index ***/
#define INLJ2O 0x2	/*** T2 outer use T1 index ***/
#define BNLJ1O 0x3	/*** T1 outer use T2 block ***/
#define BNLJ2O 0x4	/*** T2 outer use T1 block ***/

#define OK_2INDEX  0x1
#define NO_2INDEX  0x2
#define NO_RECORDS 0x3

/*****	CATALOG >>> contains information on Catalog File, containing Number of Tables, Tables List *****/
class catalog {
	char	catalog_file[FILE_NAME_LENGTH];
	int	num_of_tables;
	table 	*tlist;				// list of tables in this catalog
	buffer	*buf_mgr;			// one buffer for each catalog
	stm	*stm_catalog;			// to read and write to a catalog file
	class my_index	*tbl_index ;

	int read_catalog_file();
public:
	catalog(char *catalog_file, int num_blocks);
	~catalog();

	int add_table(tableinfo *tbl1);
	int insert_record(char *tablename, int numvalues, int *arr);
	int select_call(char *tname1, vector<attrib_field*> attlist, condition *c1);
	int join_tables(char *tname1, char *tname2, vector<attrib_field*> attlist, condition *jqry, condition *c1);
	int create_index(char *tname, char *iname, char *attrib, int dup_flag);
	int commit();

	int fill_the_attribute(table *t1, attrib_field *fld, int type);
	/*** To fill up the offsets in the condition ***/
	int fill_the_condition(table *t1, condition *c1);
	int fill_the_condition(table *t1, table *t2, condition *c1);
	/*** To fill up the offsets in the attribute list ***/
	int fill_the_attriblist(table *t1, vector<attrib_field*> attlist);
	int fill_the_attriblist(table *t1, table *t2,  vector<attrib_field*> attlist);

	int fill_the_attribute(block *b1, int r1, attrib_field *fld, int type);
	/*** To fill up the values in the condition ***/
	int fill_the_condition(block *b1, int r1, condition *c1);
	int fill_the_condition(block *b1, int r1, block *b2, int r2, condition *c1);
	/*** To fill up the values in the attribute list ***/
	int fill_the_attriblist(block *b1, int r1, vector<attrib_field*> attlist);
	int fill_the_attriblist(block *b1, int r1, block *b2, int r2, vector<attrib_field*> attlist);

	/*** To print the records after validation ***/
	void validate_print_records(table *t1, vector<attrib_field*> attlist, condition *c1);
	void validate_print_records(table *t1, table *t2,  vector<attrib_field*> attlist, condition *jqry, condition *c1);

	/*** to print the information abuot the catalog ***/
	int 	print();
	int	print(char *tablename);
	int	print_index(char *iname);
	void	print_buffers();
	int	print_hit_rate();
	
	int estimate_qplan(table *t1,condition *qry);
	int estimate_qplan(table *t1,table *t2,condition *qry);
};

#define JOIN_TYPE 	0
#define NON_JOIN_TYPE	1
int jDebugON=0;


catalog :: catalog(char *filename, int num_blocks)
{
	strcpy(catalog_file, filename);
	buf_mgr 	= new buffer(num_blocks);
	stm_catalog 	= new stm(catalog_file, CATALOG_TYPE_FILE);
	tbl_index 	= new my_index();
	tlist		= NULL;

	read_catalog_file();	
}

int catalog :: read_catalog_file()
{
	tableinfo tinfo;
	int	i=0, offset=0;
	int	num=0;

	if(DISK_FILE == stm_catalog->get_file_type())
	{
		stm_catalog->read_buffer(0, (char *) &num, sizeof(num));
		offset += sizeof(num);
		for(i=0; i<num; i++)
		{
			stm_catalog->read_buffer(offset, (char *)&tinfo, sizeof(tableinfo));
			DBGPRINTN(tinfo.num_of_records);
			add_table(&tinfo);
			offset += sizeof(tableinfo);
		}
		DBGPRINTF("DISK CATALOG FILE: %d tables are created using the catalog file\n", num);
		return(0);

	}
	else {
		DBGPRINTF("NEW CATALOG FILE: there are no tables created yet\n");
		return(0);
	}
}


int catalog :: add_table( tableinfo *tinfo1)
{

	table *t1=NULL, *temp=NULL;

	DBGPRINTN(tinfo1->num_of_attribs);
	DBGPRINTS(tbl_index);

	/***  Check if Table exists already ***/
	if( NULL != tbl_index->get_key(tinfo1->table_name) )
	{
		FATAL("Table %s already present in the database\n",tinfo1->table_name);
		return(INVALID_DATA);
	}

	t1 =  new table(tinfo1, buf_mgr);
	DBGPRINTS(t1->get_table_name());
	tbl_index->insert_key(tinfo1->table_name, (void *) t1);

	/*** Adding to the list of tables ***/
	if(tlist == NULL)
	{
		tlist = t1;
	} else {
		temp = tlist;
		while(temp->next!=NULL)	
		{
			temp=temp->next;
			DBGPRINTN(temp->next);
		}
		temp->next = t1;
	}


	DBGPRINTS(t1->get_table_name());
	
	num_of_tables++;
	return(0);
}

int catalog :: insert_record(char *tablename, int numvalues, int *arr)
{

	table *t1;
	if( NULL == (t1 = (table *) tbl_index->get_key(tablename)) )
	{
		FATAL("Table key not found for %s\n", tablename);
		DBGPRINTN(numvalues);
		DBGPRINTS(tablename);
		return DATA_NOT_FOUND;
	}

	DBGPRINTN(numvalues);
	if(numvalues != t1->no_of_attribs())
	{
		FATAL("No. of values not equal to No. of attribs\n");
		DBGPRINTN(numvalues);
		DBGPRINTS(tablename);
		DBGPRINTN(t1->no_of_attribs());
		return DATA_NOT_FOUND;
	}

	t1->insert_record(arr);
	return(0);
}

int catalog :: create_index(char *tname, char *iname, char *attrib, int dup_flag)
{
	table *t1;

	if( NULL == (t1 = (table *) tbl_index->get_key(tname)) )
	{
		FATAL("Table key not found for %s\n", tname);
		return DATA_NOT_FOUND;
	}

	DBGPRINTS(tname);
	DBGPRINTS(iname);
	DBGPRINTS(attrib);
	if(t1->add_index(iname, attrib, dup_flag))
	{
		FATAL("Error in adding Index for %s in Table %s\n", attrib, tname);
		return(DATA_NOT_FOUND);
	}

	return(0);
}

/*** START OF Functions to update the offsets only ***/

int catalog :: fill_the_attribute(table *t1, attrib_field *fld, int type)
{
	int offset=-1, status =1;

	if(type!=JOIN_TYPE || strcmp(t1->get_table_name(),fld->tablename)==0)
	{
		offset = t1->get_attrib_offset(fld->attribname);
		fld->offset = offset;
		(-1 == offset)? status=1:status=0;
	}

	return status;
}

int catalog :: fill_the_attriblist(table *t1, vector<attrib_field*> attlist)
{
	for(int ii=0;ii<attlist.size();ii++)
		if (fill_the_attribute(t1, attlist[ii], NON_JOIN_TYPE))
			return 1;
	return 0;
}

int catalog :: fill_the_attriblist(table *t1, table *t2,  vector<attrib_field*> attlist)
{
	for(int ii=0;ii<attlist.size();ii++)
		if (fill_the_attribute(t1, attlist[ii], JOIN_TYPE))
			if(fill_the_attribute(t2,attlist[ii], JOIN_TYPE))
				return 1;
	return 0;
}

int catalog :: fill_the_condition(table *t1, condition *c1)
{
	int status = 0;

	if(NULL == c1)
		return 0;

	if(c1->isfield1==ISATTRIB_TYPE || c1->isfield1==ISTATTRIB_TYPE)
	{
		if(fill_the_attribute(t1, (attrib_field*)c1->f1, NON_JOIN_TYPE))
		{
			((attrib_field *)c1->f1)->print();
			status|=1;
		}
	}
	else if(c1->isfield1==ISCONDITION_TYPE)
		status |= fill_the_condition(t1, (condition*)c1->f1);

	if(c1->isfield2==ISATTRIB_TYPE || c1->isfield2==ISTATTRIB_TYPE)
		if(fill_the_attribute(t1, (attrib_field*)c1->f2, NON_JOIN_TYPE))
		{
			((attrib_field *)c1->f2)->print();
			status|=1;
		}
	else if(c1->isfield2==ISCONDITION_TYPE)
		status |= fill_the_condition(t1, (condition*)c1->f2);

	return(status);
}

int catalog :: fill_the_condition(table *t1, table *t2, condition *c1)
{
	int status = 0;

	if(NULL == c1)
		return 0;

	if(c1->isfield1==ISTATTRIB_TYPE)
		if(fill_the_attribute(t1, (attrib_field*)c1->f1, JOIN_TYPE))
			if(fill_the_attribute(t2, (attrib_field*)c1->f1, JOIN_TYPE))
				status|=1;
	else if(c1->isfield1==ISCONDITION_TYPE)
		status |= fill_the_condition(t1, t2, (condition*)c1->f1);

	if(c1->isfield2==ISTATTRIB_TYPE)
		if(fill_the_attribute(t1, (attrib_field*)c1->f2, JOIN_TYPE))
			if(fill_the_attribute(t2, (attrib_field*)c1->f2, JOIN_TYPE))
				status|=1;
	else if(c1->isfield2==ISCONDITION_TYPE)
		status |= fill_the_condition(t1, t2, (condition*)c1->f2);

	return(status);
}

/*** END Of Functions to update the offsets only ***/



/*** START Of Functions to update the values only ***/

int catalog :: fill_the_attribute(block *b1, int r1, attrib_field *fld, int type)
{
	int offset=0, status =1;
	int *aRow;

	if(type != JOIN_TYPE || strcmp(b1->get_file_name(),fld->tablename)==0)
	{
		aRow =  b1->get_row(r1);
		switch(fld->agg_type) {
			case ISNULL_TYPE:
				fld->value = aRow[fld->offset];	 
				break;
			case SUM:
			case AVG:
				fld->value += aRow[fld->offset]; 
				break;
			case MIN:
				if (fld->value > aRow[fld->offset]) 
				fld->value=aRow[fld->offset];
				break;
			case MAX:
				if (fld->value < aRow[fld->offset])
					fld->value=aRow[fld->offset];
				break;
			case COUNT:
				fld->value++;
				break;
		}
		return 0;
	}
	else
		return 1;

}

int catalog :: fill_the_condition(block *b1, int r1, condition *c1)
{
	int status = 0;

	if(NULL == c1)
		return 0;

	if(c1->isfield1==ISATTRIB_TYPE || c1->isfield1==ISTATTRIB_TYPE)
		if(fill_the_attribute(b1, r1, (attrib_field*)c1->f1, NON_JOIN_TYPE))
			status|=1;
	else if(c1->isfield1==ISCONDITION_TYPE)
		status |= fill_the_condition(b1, r1, (condition*)c1->f1);

	if(c1->isfield2==ISATTRIB_TYPE || c1->isfield2==ISTATTRIB_TYPE)
		if(fill_the_attribute(b1, r1, (attrib_field*)c1->f2, NON_JOIN_TYPE))
			status|=1;
	else if(c1->isfield2==ISCONDITION_TYPE)
		status |= fill_the_condition(b1, r1, (condition*)c1->f2);

	return(status);
}

int catalog :: fill_the_condition(block *b1, int r1, block *b2, int r2, condition *c1)
{
	int status = 0;

	if(NULL == c1)
		return 0;

	if(c1->isfield1==ISTATTRIB_TYPE)
	{
		if(fill_the_attribute(b1, r1, (attrib_field*)c1->f1, JOIN_TYPE))
			if(fill_the_attribute(b2, r2, (attrib_field*)c1->f1, JOIN_TYPE))
				status|=1;
	}
	else if(c1->isfield1==ISCONDITION_TYPE)
	{
		status |= fill_the_condition(b1, r1, b2, r2, (condition*)c1->f1);
	}

	if(c1->isfield2==ISTATTRIB_TYPE)
	{
		if(fill_the_attribute(b1, r1, (attrib_field*)c1->f2, JOIN_TYPE))
			if(fill_the_attribute(b2, r2, (attrib_field*)c1->f2, JOIN_TYPE))
				status|=1;
	}
	else if(c1->isfield2==ISCONDITION_TYPE)
	{
		status |= fill_the_condition(b1, r1, b2, r2, (condition*)c1->f2);
	}

	return(status);
}

int catalog :: fill_the_attriblist(block *b1, int r1, vector<attrib_field*> attlist)
{
	for(int ii=0;ii<attlist.size();ii++)
		if (fill_the_attribute(b1, r1, attlist[ii], NON_JOIN_TYPE))
			return 1;
	return 0;
}

int catalog :: fill_the_attriblist(block *b1, int r1, block *b2, int r2, vector<attrib_field*> attlist)
{
	for(int ii=0;ii<attlist.size();ii++)
		if (fill_the_attribute(b1, r1, attlist[ii], JOIN_TYPE))
			if(fill_the_attribute(b2, r2, attlist[ii], JOIN_TYPE))
				return 1;
	return 0;
}

/*** END Of Functions to update the values only ***/

int catalog :: select_call(char *tname1, vector<attrib_field*> attlist, condition *c1)
{
	table *t1=(table *) tbl_index->get_key(tname1);
	
	if(NULL==t1)
	{
		FATAL("Table key not found for %s\n", tname1);
		return(DATA_NOT_FOUND);
	}

	if(strcmp(attlist[0]->attribname, "*")==0)
	{
		int i=0;
		attrib_field *f1 = attlist[0], 	*temp_field = NULL;
		tableinfo *tinfo = t1->tbl_info();
		for(i=0; i<tinfo->num_of_attribs; i++)
		{
			temp_field = new attrib_field();
			strcpy(temp_field->tablename, tinfo->table_name);
			strcpy(temp_field->attribname, tinfo->attrib_names[i]);
			attlist.push_back(temp_field);
			if(attlist[0]->agg_type==COUNT)
			{
				temp_field->agg_type = COUNT;
				break;
			}
		}

		attlist.erase(attlist.begin());
		delete f1;
	}

	if(fill_the_attriblist(t1, attlist))
	{
		FATAL("Attributes in the select list not found\n");
		print_attriblist(attlist);
		return(DATA_NOT_FOUND);

	}

	if(fill_the_condition(t1, c1))
	{
		FATAL("Attributes in the query not found\n");
		if(c1!=NULL) c1->print();
		return(DATA_NOT_FOUND);
	}

	validate_print_records(t1, attlist, c1);
	return (0);

}

int catalog :: join_tables(char *tname1, char *tname2, vector<attrib_field*> attlist, condition *jqry, condition *c1)
{
	table *t1=(table *) tbl_index->get_key(tname1);
	table *t2=(table *) tbl_index->get_key(tname2);
	DBGPRINTS(tname1);
	DBGPRINTS(tname2);
	
	if((NULL==t1 || NULL==t2))
	{
		FATAL("Table key not found for %s\n", (NULL==t1)?(tname1):(tname2));
		return(DATA_NOT_FOUND);
	}

	if(strcmp(attlist[0]->attribname, "*")==0)
	{
		int i=0;
		attrib_field *f1 = attlist[0], 	*temp_field = NULL;
		tableinfo *tinfo = t1->tbl_info();
		for(i=0; i<tinfo->num_of_attribs; i++)
		{
			temp_field = new attrib_field();
			strcpy(temp_field->tablename, tinfo->table_name);
			strcpy(temp_field->attribname, tinfo->attrib_names[i]);
			attlist.push_back(temp_field);
			if(attlist[0]->agg_type==COUNT)
			{
				temp_field->agg_type = COUNT;
				goto count_only;
			}
		}

		tinfo = t2->tbl_info();
		for(i=0; i<tinfo->num_of_attribs; i++)
		{
			temp_field = new attrib_field();
			strcpy(temp_field->tablename, tinfo->table_name);
			strcpy(temp_field->attribname, tinfo->attrib_names[i]);
			attlist.push_back(temp_field);
		}

count_only:

		attlist.erase(attlist.begin());
		delete f1;
	
	}

	if(fill_the_attriblist(t1,t2, attlist))
	{
		FATAL("Attributes in the select list not found\n");
		print_attriblist(attlist);
		return(DATA_NOT_FOUND);

	}

	if(fill_the_condition(t1,t2, jqry))
	{
		FATAL("Attributes in the join query not found\n");
		if(c1!=NULL) c1->print();
		return(DATA_NOT_FOUND);
	}

	if(fill_the_condition(t1, t2, c1))
	{
		FATAL("Attributes in the query not found\n");
		if(c1!=NULL) c1->print();
		return(DATA_NOT_FOUND);
	}

	validate_print_records(t1, t2, attlist, jqry, c1);

}

void catalog :: validate_print_records(table *t1, vector<attrib_field*> attlist, condition *c1)
{
	
	int i=0, j=0, k=0, l=0;
	int agg_type=0, count=0;
	int use_index=0;

	block *b1=NULL;

	print_attriblist(attlist);
#ifdef DEBUG
	if(c1!=NULL) c1->print();
#endif

	for(int ii=0;ii<attlist.size();ii++)
	{
		if(attlist[ii]->agg_type!=ISNULL_TYPE)
		{
			agg_type=1;
			break;
		}
	}

	if(c1!=NULL)
	{
		switch(estimate_qplan(t1,c1)) {
			case OK_2INDEX:
				use_index=1;
				break;
			case NO_2INDEX:
				use_index=0;
				break;
			case NO_RECORDS:
				return;
		}
	}

	if(use_index && (c1->op==OP_EQ)){
		PRINT("\nUsing Index ...\n");
		bplustree *i1=NULL;

		i1=t1->find_index(((attrib_field*)c1->f1)->offset);
		if(i1==NULL)	FATAL("Index not found !!!");

		vector<int> tableBloks = i1->getTableBloks(((attrib_field*)c1->f2)->value, iDebugON);
		t1->reset_diskio();
		i1->reset_diskio();
		for(i=0; i<tableBloks.size(); i++) {
			b1=t1->get_block_num(tableBloks[i]);
			for(k=0;k<b1->records_present();k++) {
				fill_the_condition(b1, k, c1);
				if(validate_cquery(c1)) {
					fill_the_attriblist(b1, k, attlist);
					if(!agg_type)
						print_attriblist_values(attlist);
					count++;
				}
			}
		}
		PRINT("Table I/O: %d, Index I/O: %d\n",t1->get_io_count(), i1->get_io_count());
	} else {
		PRINT("\nReading Blocks ...\n");
		t1->reset_diskio();
		for(i=0; i<t1->no_of_blocks(); i++) {
			b1 = t1->get_block_num(i);
			for(k=0;k<b1->records_present();k++) {
				fill_the_condition(b1, k, c1);
				if(validate_cquery(c1)) {
					fill_the_attriblist(b1, k, attlist);
					if(!agg_type)
						print_attriblist_values(attlist);
					count ++;
				}
			}
		}
		PRINT("Table I/O: %d \n",t1->get_io_count());
	}

	if(agg_type)
		print_attriblist_values(attlist, count);
}

int get_value(table *t1, int* row, condition* c)
{
	if(c==NULL)
	{
		return 0;
	}
	if(c->isfield1 == ISTATTRIB_TYPE)
	{
		if(strcmp(((attrib_field*)c->f1)->tablename,t1->get_table_name()) == 0)
		{
			return row[((attrib_field*)c->f1)->offset];
		}
	}
	else if(c->isfield2 == ISTATTRIB_TYPE)
	{
		if(strcmp(((attrib_field*)c->f2)->tablename,t1->get_table_name()) == 0)
		{
			return row[((attrib_field*)c->f2)->offset];
		}
	}
}

void catalog :: validate_print_records(table *t1, table *t2,  vector<attrib_field*> attlist, condition *jqry, condition *c1)
{
	
	int i=0, j=0, k=0, l=0;
	int agg_type=0, count=0;
	block *b1=NULL, *b2=NULL;
	table *inner=t2, *outer=t1;
	bplustree *ii;
	int is_INLJ=0;

#ifdef DEBUG
	if(c1!=NULL) c1->print();
#endif
	print_attriblist(attlist);

	for(int ii=0;ii<attlist.size();ii++)
	{
		if(attlist[ii]->agg_type!=ISNULL_TYPE)
		{
			agg_type=1;
			break;
		}
	}
	
	if(jqry!=NULL)
	{
		switch(estimate_qplan(t1,t2,jqry)) {
			case INLJ1O:
				is_INLJ=1;
			case BNLJ1O:
				outer = t1;
				inner = t2;
				break;
			case INLJ2O:
				is_INLJ=1;
			case BNLJ2O:
				outer = t2;
				inner = t1;
				break;
		}
	}

	if(jDebugON)	PRINT("\n%s: Using %s as Outer, %s as Inner\n",(is_INLJ)?("INLJ"):("BNLJ"), outer->get_table_name(), inner->get_table_name());

	if(is_INLJ)
	{
		if(jDebugON)		PRINT("\nINLJ started ...\n");
		if(strcmp(outer->get_table_name(),((attrib_field *)jqry->f1)->tablename)==0)
			ii=inner->find_index(((attrib_field*)jqry->f1)->offset);
		else
			ii=inner->find_index(((attrib_field*)jqry->f2)->offset);

		outer->reset_diskio();
		inner->reset_diskio();
		ii->reset_diskio();
		for(i=0; i<outer->no_of_blocks(); i++)
		{
			b1 = outer->get_block_num(i);
			b1->pin();
			for(k=0;k<b1->records_present();k++)
			{
				int *oRow = b1->get_row(k);
				int key = get_value(outer, oRow, jqry);
				vector<int> innerBlocks =  ii->getTableBloks(key, iDebugON);

				for(j=0;j<innerBlocks.size();j++)
				{
					b2 = inner->get_block_num(innerBlocks[j]);
					for(l=0;l<b2->records_present();l++)
					{
						fill_the_condition(b1, k, b2, l, c1);
						if(c1==NULL || validate_cquery(c1))
						{
							fill_the_condition(b1, k, b2, l, jqry);
							if(jqry==NULL || validate_cquery(jqry))
							{
								fill_the_attriblist(b1, k, b2, l, attlist);
								if(!agg_type)
									print_attriblist_values(attlist);
								count ++;
							}
						}
					}
				}
			}
			b1->unpin();
		}
		PRINT("INLJ: Table I/O Outer: %d, Inner:%d, Index I/O: %d\n", outer->get_io_count(), inner->get_io_count(), ii->get_io_count());
	}
	else
	{
		if(jDebugON)		PRINT("\nBNLJ started ...\n");
		outer->reset_diskio();
		inner->reset_diskio();
		for(i=0; i<outer->no_of_blocks(); i++)
		{
			b1 = outer->get_block_num(i);
			b1->pin();
			for(j=0; j<inner->no_of_blocks(); j++)
			{
				for(k=0;k<b1->records_present();k++)
				{
					fill_the_condition(b1, k, c1);
					if(c1==NULL || validate_cquery(c1))
					{
						b2 = inner->get_block_num(j);
						for(l=0;l<b2->records_present();l++)
						{
							fill_the_condition(b1, k, b2, l, jqry);
							if(jqry==NULL || validate_cquery(jqry))
							{
								fill_the_attriblist(b1, k, b2, l, attlist);
								if(!agg_type)
									print_attriblist_values(attlist);
								count ++;
							}
						}
					}
				
				}
			}
			b1->unpin();
		}
		PRINT("BNLJ: Table I/O Outer: %d, Inner:%d\n", outer->get_io_count(), inner->get_io_count());
	}

	if(agg_type)
		print_attriblist_values(attlist, count);
}

int catalog :: print()
{
	table *tbl_ptr;

	HEAD("CATALOG >>> %s\n", catalog_file);		
	SHEAD("Number of Tables >>> %d\n", num_of_tables);

	tbl_ptr = tlist;

	while(tbl_ptr!=NULL) {
		tbl_ptr->print_stats();
		tbl_ptr = tbl_ptr->next;
	}

	buf_mgr->print_stats();

	return(0);
}

int catalog :: print(char *tablename)
{
	table *t1;
	if( NULL == (t1 = (table *) tbl_index->get_key(tablename)) )
	{
		FATAL("Table key not found for %s\n", tablename);
		DBGPRINTS(tablename);
		return DATA_NOT_FOUND;
	}

	if(t1->print())
	{
		FATAL("Error in printing table %s\n",tablename);
		return INTERNAL_ERROR;
	}

	return(0);
}

int catalog :: print_index(char *iname)
{
	table *t1;

	t1 = tlist;

	while(t1!=NULL)
	{
		DBGPRINTS(t1->get_table_name());
		if(NULL == t1->find_index(iname))
		{
			t1 =  t1->next;
			continue;
		}
		else
		{
			t1->print_index(iname);
			return(0);
		}
	}

	DBGPRINTS(iname);
	FATAL("Index %s not found\n", iname);
	return(DATA_NOT_FOUND);

}

int catalog :: commit()
{
	table *tbl_ptr;
	int offset = 0;

	buf_mgr->commit();

	stm_catalog->write_buffer( offset, (char *) &num_of_tables, sizeof(num_of_tables));
	offset += sizeof(num_of_tables);

	tbl_ptr = tlist;
	while(tbl_ptr!=NULL)
	{
		tbl_ptr->commit();	

		struct tableinfo *t_info = tbl_ptr->tbl_info();

		DBGPRINTS(tbl_ptr->get_table_name());
		DBGPRINTN(t_info->num_of_records);
		DBGPRINTN(sizeof(struct tableinfo));

		stm_catalog->write_buffer( offset, (char *) t_info, sizeof(struct tableinfo));
		offset += sizeof(tableinfo);
		tbl_ptr =  tbl_ptr->next;
	}

	stm_catalog->flush();

	return(0);

}

int catalog :: print_hit_rate()
{
	buf_mgr->print_stats();
	return(0);
}

/***	1. Commit all the stm, buf_mgr, table classes
	2. Delete all the classes created using new in this file ***/

catalog :: ~catalog()
{
	table *temp, *next;

	commit();
	delete buf_mgr;

	temp = tlist;
	while(temp != NULL)
	{
		next = temp->next;
		delete temp;
		temp = next;
	}

	delete stm_catalog;
	delete tbl_index;

}

void catalog :: print_buffers()
{
	buf_mgr->print();
}

int catalog :: estimate_qplan(table *t1,table *t2,condition *qry)
{
	int index_t1=0;
	int index_t2=0;
	int blocks_t1 =0;
	int blocks_t2=0;
	bplustree* i1=NULL;
	bplustree* i2=NULL;
	int temp1_index=9999;
	int temp2_index=9999;
	int temp1_b=9999;
	int temp2_b=9999;

	if(qry==NULL)
		return BNLJ2O;

	if(strcmp(t1->get_table_name(), ((attrib_field *)qry->f1)->tablename)==0)
		i1=t1->find_index(((attrib_field*)qry->f1)->offset);
	else
		i1=t1->find_index(((attrib_field*)qry->f2)->offset);

	if(strcmp(t2->get_table_name(),((attrib_field *)qry->f1)->tablename)==0)
		i2=t2->find_index(((attrib_field*)qry->f1)->offset);
	else
		i2=t2->find_index(((attrib_field*)qry->f2)->offset);

	if (i2 != NULL)
	{
		temp1_index = t1->no_of_records()*(i2->height+1)+t1->no_of_blocks();
	}
	if(i1 != NULL)
	{
		temp2_index = t2->no_of_records()*(i1->height+1)+t2->no_of_blocks();
	}

	temp1_b=t1->no_of_blocks()*t2->no_of_blocks()+t1->no_of_blocks();
	temp2_b=t2->no_of_blocks()*t1->no_of_blocks()+t2->no_of_blocks();
	
	if(jDebugON)
	{
		PRINT("\nT1 outer: T2 inner = %d\n",(temp1_b==9999)?-1:temp1_b);	
		PRINT("T2 outer: T1 inner = %d\n",(temp2_b==9999)?-1:temp2_b);	
		PRINT("T1 outer: I2 inner = %d\n",(temp1_index==9999)?-1:temp1_index);	
		PRINT("T2 outer: I1 inner = %d\n",(temp2_index==9999)?-1:temp2_index);	
	}

	if(temp1_index<temp2_index && temp1_index<temp1_b && temp1_index<temp2_b)
		return INLJ1O;
	if(temp2_index<temp1_index && temp2_index<temp1_b && temp2_index<temp2_b)
		return INLJ2O;
	if(temp1_b<temp1_index && temp1_b<temp2_index && temp1_b<temp2_b)
		return BNLJ1O;
	if(temp2_b<temp1_index && temp2_b<temp2_index && temp2_b<temp1_b)
		return BNLJ2O;
}

int catalog :: estimate_qplan(table *t1,condition *qry)
{
	bplustree *i1=NULL;
	attrib_field *f1=NULL;


	if(qry==NULL)
		return NO_2INDEX;

	f1 = (attrib_field *)qry->f1;

	i1=t1->find_index(f1->offset);

	if(i1 == NULL) {
		PRINT("\nIndex not found for attrib in  %s", t1->get_table_name());
		return NO_2INDEX;
	} else {
		PRINT("\nIndex %s found for  attrib in %s",i1->iname, t1->get_table_name());
	}

	int temp = ((attrib_field*)qry->f2)->value;
	int offset=((attrib_field*)qry->f1)->offset;
	int max = (t1->tbl_info())->max[offset];
	int min = (t1->tbl_info())->min[offset];
	int range = max - min;

	if(qry->op==OP_EQ) {
		PRINT("\nESTIMATE:(1%) Operator is = , Ok to use Index\n");
		return OK_2INDEX;
	} else if(i1->height+1>=t1->no_of_blocks()) {
		PRINT("\nESTIMATE: Index height is equal to number of blocks in table\n");
		return NO_2INDEX;
	}
	if((qry->op==OP_GR) && (qry->isfield2==ISNUMBER_TYPE)) {
		int est = (int)(max-temp)*100/range;
		if(jDebugON)		PRINT("MAX %d, MIN %d, QVAL %d\n", max, min, temp);
		if(jDebugON)		PRINT("Estimate = %d% Blocks",est);
		if(est<=0) {
			PRINT("\nESTIMATE:(%d% Blocks) Value specified is exceeds range\n",est);
			return NO_RECORDS;
		} else if(est>=50) {
			PRINT("\nESTIMATE: (%d% Blocks) No. of Blocks more than half, skip using Index\n",est);
			return NO_2INDEX;
		}
	} else if((qry->op==OP_LS) && (qry->isfield2==ISNUMBER_TYPE)) {
		int est = (int)(temp-min)*100/range;
		if(jDebugON)		PRINT("MAX %d, MIN %d, QVAL %d\n", max, min, temp);
		if(jDebugON)		PRINT("Estimate = %d% Blocks",est);
		if(est<=0) {
			PRINT("\nESTIMATE:(%d% Blocks) Value specified is exceeds range\n",est);
			return NO_RECORDS;
		} else if(est>=50) {
			PRINT("\nESTIMATE: (%d% Blocks) No. of Blocks more than half, skip using Index\n",est);
			return NO_2INDEX;
		}
	} 

	return OK_2INDEX;
}
